import uuid
from typing import Any
from uuid import UUID

import pytest
from httpx import AsyncClient
from httpx._status_codes import codes  # noqa
from sqlalchemy import delete, insert
from sqlalchemy.ext.asyncio import AsyncSession

from recruit.adapter.database.models import User
from dataclasses import dataclass


@dataclass
class UserTest:
    id: uuid.UUID
    user_id: str


async def _delete(session: AsyncSession, **kwargs: Any) -> None:
    stmt = delete(User).filter_by(**kwargs)
    await session.execute(stmt)
    await session.commit()


async def _create(session: AsyncSession, **kwargs: Any) -> UserTest:
    stmt = insert(User).values(**kwargs).returning(User)
    result = (await session.execute(stmt)).scalar_one()
    user = UserTest(
        id=result.id,
        user_id=result.user_id
    )
    await session.commit()
    return user


@pytest.mark.parametrize(
    'data', [
        {
            'userId': 123456789,
            'username': '@username'
        }
    ]
)
async def test_create_user(
        data: dict[str, Any],
        app_client: AsyncClient,
        db_session: AsyncSession
) -> None:
    response = await app_client.post('/api/users/', json=data)
    assert response.status_code == codes.CREATED
    result = response.json()
    assert sorted(tuple(result)) == sorted(('id', 'userId', 'username'))
    assert result['userId'] == data['userId']
    assert result['username'] == data['username']
    assert UUID(result['id'], version=4)
    response = await app_client.post('/api/users/', json=data)
    assert response.status_code == codes.BAD_REQUEST
    result = response.json()
    assert result.get('detail')
    assert result['detail'] == 'Пользователь уже существует!'
    await _delete(db_session, user_id=str(data['userId']))


@pytest.mark.parametrize(
    'data', [
        {
            'user_id': '123456789',
            'username': '@username'
        }
    ]
)
async def test_get_user(
        data: dict[str, Any],
        app_client: AsyncClient,
        db_session: AsyncSession
) -> None:
    user = (await _create(db_session, **data))
    response = await app_client.get(f'/api/users/{user.user_id}/')
    assert response.status_code == codes.OK
    result = response.json()
    assert sorted(tuple(result)) == sorted(('id', 'userId', 'username'))
    assert result['id'] == str(user.id)
    assert result['userId'] == int(data['user_id'])
    assert result['username'] == data['username']
    response = await app_client.get(f'/api/users/0/')
    assert response.status_code == codes.NOT_FOUND
    await _delete(db_session, user_id=str(data['user_id']))


@pytest.mark.parametrize(
    'data', [
        {
            'user_id': '123456789',
            'username': '@username'
        }
    ]
)
async def test_update_user(
        data: dict[str, Any],
        app_client: AsyncClient,
        db_session: AsyncSession
) -> None:
    user_id = (await _create(db_session, **data)).id
    new_username = '@newusername'
    response = await app_client.patch(
        f'/api/users/{user_id}/',
        json={
            'username': new_username
        }
    )
    assert response.status_code == codes.OK
    result = response.json()
    assert sorted(tuple(result)) == sorted(('id', 'userId', 'username'))
    assert result['id'] == str(user_id)
    assert result['userId'] == int(data['user_id'])
    assert result['username'] == new_username
    response = await app_client.patch(
        f'/api/users/{uuid.uuid4()}/',
        json={
            'username': new_username
        }
    )
    assert response.status_code == codes.NOT_FOUND
    await _delete(db_session, user_id=str(data['user_id']))
